

// 测试监听事件 _listensock 的作用
// select 检测事件就绪：
//      1. Accepter ---> (监听事件 _listensock 就绪) 多个 telnet 127.0.0.1 8081 连接的时候，_listensock 得获取，并设置进 rfds 中去
//      2. recver ---> (IO事件，除了 _listensock 以外的 sock 就绪，一般是为发送信息过来的 fd) 已经连接上的 fd 发送信息的情况
//

#pragma once

#include <cstring>
#include <iostream>
#include <string>
#include <functional>
#include "sock.hpp"

namespace select_ns{
    static const int defaultport = 8081;
    static const int fd_num = sizeof(fd_set) * 8;     // 1024
    static const int defaultfd = -1;

    using func_t = std::function<std::string(const std::string&)>;

    class SelectServer{
    public:
        SelectServer(func_t f, int port = defaultport) 
            : func(f), _port(port), _listensock(-1), fdarray(nullptr)
        {}

        void initServer(){
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);

            fdarray = new int[fd_num];                                 // 开辟空间
            for(int i = 0; i < fd_num; i++) fdarray[i] = defaultfd;    // 清空
            fdarray[0] = _listensock;                                  // _listensock 也得设置进去
        }

        void Print(){
            std::cout << "fd list: " << std::endl;
            for(int i = 0; i < fd_num; i++) 
                if(fdarray[i] != defaultfd)
                    std::cout << fdarray[i] << ' ';
            std::cout << std::endl;
        }

        void Accepter(int listensock){
            logMessage(DEBUG, "in Accept");

            // 走到这里的时候，下面的acccept是不会阻塞的
            // select 告诉我 _listensock 读事件就绪了
            std::string clientip;
            uint16_t clientport = 0;
            // 新的 sock 拿上来后，队列里面就没有了 ---> 新连接从 telnet 断开后就不会一直打印 have event ready! 了，因为新 sock 被拿走了
            int sock = Sock::Accept(_listensock, &clientip, &clientport);
            if(sock < 0) return ;
            logMessage(NORMAL, "accept success [%s : %d]", clientip.c_str(), clientport);

            // sock 我们能直接 recv / read 吗？ 不能，整个代码，只有select有资格检测事件是否就绪
            // 将新的sock托管给select !
            // 将新 sock 托管给 select 的本质，其实就是将 sock，添加到 fdarray 数组中即可(HandlerEvent 函数调用完成之后，start函数里面的再一次循环就会把新来的 sock, FD_SET(fdarray[i], &rfds) 给设置进去)
            // 遍历 fdarray 数组看最后一个可以填入 sock 的位置在哪里（用 int i = 0 和 for 循环来探测, 注意可能数组满了）
            int i = 0;
            for(i = 0; i < fd_num; i++){
                if(fdarray[i] != defaultfd) continue;
                else break;
            }
            if(i == fd_num){
                logMessage(WARNING, "server is full, please wait");
                close(sock);
            }
            else fdarray[i] = sock;

            Print();

            logMessage(DEBUG, "out Accept");
        }

        void Recver(int sock, int pos){
            logMessage(DEBUG, "in Recver");

            // 1. 读取 request
            // 这里的读取方式是有问题的(什么能不能读取到一个完整的报文之类的问题)
            char readbuffer[1024];
            ssize_t s = recv(sock, readbuffer, sizeof(readbuffer) - 1, 0);
            if(s > 0){
                readbuffer[s] = 0;
                logMessage(NORMAL, "clent# %s", readbuffer);
            }else if(s == 0){
                close(sock);
                fdarray[pos] = defaultfd;
                logMessage(ERROR, "client quit: %s", strerror(errno));
            }

            // 2. 处理 request
            std::string response = func(readbuffer);

            // 3. 返回 response
            // write bug
            write(sock, response.c_str(), response.size());

            logMessage(DEBUG, "out Recver");
        }

        void HandlerEvent(fd_set& rfds){
            // 判断是什么事件就绪了：是监听事件_listensock, 还是除了_listensock之外的IO事件就绪了
            for(int i = 0; i < fd_num; i++){
                if(fdarray[i] == defaultfd) continue;

                if(FD_ISSET(fdarray[i], &rfds) && fdarray[i] == _listensock){
                    // 上面的 if 判断语句里面的 i 可不能写成 0 的哦，因为 _listensock 在 fdarrray 数组里面的位置发生了变化的哦
                    Accepter(fdarray[i]);    
                }else if(FD_ISSET(fdarray[i], &rfds)){
                    Recver(fdarray[i], i);
                }else{

                }
            }
        }

        void start(){          
            // 观察下面的现象：下面设置的 struct timaval timavalue; 得放在下面的额死循环里面的哦
            // 因为一次循环之后 timavalue 就从{3， 0}前三秒阻塞再select，变成{0， 0}非阻塞一直select了
            // 放在死循环里面的话，每一次 timavalue 变成{0，0}了，会再更新成{3，0}
            // struct timeval timevalue = {3, 0};
            for(;;){
                fd_set rfds;
                // fd_set wfds;
                FD_ZERO(&rfds);
                // FD_SET(_listensock, &rfds);   // 下面循环会添加进去的(_listensock != defaultfd)

                // 最大的 fd，每一次都得遍历的哦
                int maxfd = fdarray[0];
                for(int i = 0; i < fd_num; i++){
                    if(fdarray[i] == defaultfd) continue;
                    FD_SET(fdarray[i], &rfds);       // 合法 fd 全部添加到读文件描述符集中

                    if(fdarray[i] > maxfd) maxfd = fdarray[i];
                }
                std::cout << "maxfd: " << maxfd << std::endl;

                // 下面的情况式：不设置 timavalue 的话，默认是阻塞式IO，知道有就绪的socket才select
                // int n = select(_listensock + 1, &rfds, nullptr, nullptr, nullptr);

                // 下面的情况式：先阻塞式3秒，再select
                // struct timeval timevalue = {3, 0};
                // int n = select(_listensock + 1, &rfds, nullptr, nullptr, &timevalue);


                // 一般而言，要使用 select，需要程序员自己维护一个数组 fdarray
                // 一个 select 监管多个 sock 是否就绪
                int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
                switch(n)
                {
                case 0:
                    logMessage(NORMAL, "timeout...");
                    break;
                case -1:
                    logMessage(WARNING, "select error, code: %d, err: string: %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(NORMAL, "have event ready!");
                    sleep(1); // 模拟事件处理
                    HandlerEvent(rfds); // 有新的连接就绪，就去从队列里面拿上来处理
                    // HandlerEvent(wfds);
                    break;
                }

                // 阻塞式 IO
                // std::string clientip;
                // uint16_t clientport = 0;
                // int sock = Sock::Accept(_listensock, &clientip, &clientport);
                // if(sock < 0) continue;
                // 开始进行服务器的处理逻辑
            }
        }

        ~SelectServer(){
            if(_listensock < 0) close(_listensock);
        }
    private:
        int _port;
        int _listensock;
        int* fdarray;
        func_t func;
    };
}



